﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using com.YiXiong.Util;
using System.Collections.Generic;
using System.Text;

namespace Loong.Net
{
    /// <summary>
    /// 异步Socket类 v1.0
    /// 基本功能:
    /// 异步获取指定网页文本内容
    /// </summary>
    public class AsyncRequest : IRequest
    {
        private string url;

        private string host;

        private int port = 0;

        private string localPath;

        private string query = string.Empty;

        private Socket socket;

        private StringBuilder header;

        private Encoding encoding;

        // The response from the remote device.
        private string response = string.Empty;

        private bool isCompleted = true;
        // ManualResetEvent instances signal completion.
        private ManualResetEvent connectDone = new ManualResetEvent(false);

        private ManualResetEvent sendDone = new ManualResetEvent(false);

        private ManualResetEvent receiveDone = new ManualResetEvent(false);



        public AsyncRequest(string url)
        {
            try
            {
                Uri u = new Uri(url);
                this.url = url;
                this.host = u.Host;
                this.port = u.Port;
                this.localPath = u.LocalPath;
                // Console.WriteLine(url);
                header = new StringBuilder();
                header.Append("GET ");
                header.Append(u.LocalPath);
                header.Append(u.Query);
                header.Append(" HTTP/1.1\r\n");
                header.Append("Accept: image/gif, image/x-xbitmap, image/jpeg, */*\r\n");
                header.Append("Referer: ");
                header.Append(url);
                header.Append("/\r\n");
                header.Append("Accept-Language: zh-cn\r\n");
                header.Append("Content-Type: application/x-www-form-urlencoded\r\n");
                header.Append("User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2;)\r\n");
                header.Append("Host: ");
                header.Append(host);
                header.Append("\r\n");
                header.Append("Connection: Keep-Alive\r\n");
                header.Append("Cache-Control: no-cache\r\n");
                header.Append("\r\n\r\n");
            }
            catch (UriFormatException e)
            {
                isCompleted = false;
                Log.WriteEventLog(url + "\n" + e.Message);
                return;
            }
        }

        /// <summary>
        /// 设置超时时间
        /// </summary>
        /// <param name="Timeout">秒</param>
        private void setTimeout(int Timeout)
        {
            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, Timeout * 1000);
            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, Timeout * 1000);
        }

        public Encoding Encodings
        {
            get
            {
                return this.encoding;
            }
            set
            {
                this.encoding = value;
            }
        }

        public int SendTimeout
        {
            set { setTimeout(value); }
            get { return socket.SendTimeout; }
        }

        public string HTML
        {
            get { return this.response; }
            set { this.response = value; }
        }

        /// <summary>
        /// 摘要:
        ///     获取文件名的本地操作系统表示形式。
        ///
        /// 返回结果:
        ///    一个 System.String，它包含文件名的本地操作系统表示形式。
        ///
        /// 异常:
        ///   System.InvalidOperationException:
        ///     此实例代表一个相对 URI，而此属性仅对绝对 URI 有效。
        /// </summary>
        public string LocalPath
        {
            get { return this.localPath; }
        }

        /// <summary>
        ///  摘要:
        ///     获取指定 URI 中包括的任何查询信息。
        ///
        /// 返回结果:
        ///     包含指定 URI 中包括的任何查询信息的 System.String。
        /// </summary>
        public string Query
        {
            get { return this.query; }
        }


        public bool Get()
        {
            // Connect to a remote device.
            try
            {
                // Establish the remote endpoint for the socket.
                // The name of the 
                // remote device is "host.contoso.com".
                Console.WriteLine(url);
                IPHostEntry ipHostInfo = Dns.GetHostEntry(host);
                IPAddress ipAddress = ipHostInfo.AddressList[0];
                IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);

                // Create a TCP/IP socket.
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);


                // Connect to the remote endpoint.
                socket.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), socket);
                connectDone.WaitOne();

                // Send test data to the remote device.
                Send(socket, header.ToString());
                sendDone.WaitOne();

                // Receive the response from the remote device.
                Receive(socket);
                receiveDone.WaitOne();

                // Write the response to the console.
                // Console.WriteLine("Response received : {0}", response);

                // Release the socket.
                socket.Shutdown(SocketShutdown.Both);
                socket.Close();

            }
            catch (Exception e)
            {

                Log.WriteEventLog(url + "\n" + e.StackTrace + "\n" + e.Message);
                return false;
            }
            if (!isCompleted)
            {
                return false;
            }
            return true;
        }

        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.
                Socket client = ar.AsyncState as Socket;

                // Complete the connection.
                client.EndConnect(ar);

                // Console.WriteLine("Socket connected to {0}", client.RemoteEndPoint.ToString());

                // Signal that the connection has been made.
                connectDone.Set();
            }
            catch (Exception e)
            {
                isCompleted = false;
                connectDone.Set();
                Log.WriteEventLog(url + "\n" + e.StackTrace + "\n" + e.Message);
                return;
            }
        }

        private void Send(Socket client, String data)
        {
            try
            {
                // Convert the string data to byte data using ASCII encoding.
                byte[] byteData = Encoding.Default.GetBytes(data);

                // Begin sending the data to the remote device.
                //Console.Write(Encoding.Default.GetString(byteData));
                client.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), client);
            }
            catch (Exception e)
            {
                isCompleted = false;
                sendDone.Set();
                Log.WriteEventLog(url + "\n" + e.StackTrace + "\n" + e.Message);

            }
        }

        private void SendCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.
                Socket client = ar.AsyncState as Socket;

                // Complete sending the data to the remote device.
                int bytesSent = client.EndSend(ar);
                //Console.WriteLine("Sent {0} bytes to server.", bytesSent);

                // Signal that all bytes have been sent.

                sendDone.Set();
            }
            catch (Exception e)
            {
                isCompleted = false;
                sendDone.Set();
                Log.WriteEventLog(url + "\n" + e.StackTrace + "\n" + e.Message);
                return;
            }
        }

        private void Receive(Socket client)
        {
            try
            {
                // Create the state object.
                StateObject state = new StateObject();
                state.workSocket = client;
                //Console.WriteLine(localPath);
                // Begin receiving the data from the remote device.
                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
            }
            catch (Exception e)
            {
                isCompleted = false;
                receiveDone.Set();
                Log.WriteEventLog(url + "\n" + e.StackTrace + "\n" + e.Message);
                return;
            }
        }


        private void ReceiveCallback(IAsyncResult ar)
        {

            try
            {
                // Retrieve the state object and the client socket 
                // from the asynchronous state object.
                StateObject state = ar.AsyncState as StateObject;
                Socket client = state.workSocket;

                // Read data from the remote device.
                int bytesRead = client.EndReceive(ar);

                if (bytesRead > 0)
                {
                    // There might be more data, so store the data received so far.
                    // state.sb.Append(Encoding.GetEncoding("GBK").GetString(state.buffer, 0, bytesRead));
                    state.sb.Append(Encoding.Default.GetString(state.buffer, 0, bytesRead));
                    Console.WriteLine("current Receive data size:{0}", bytesRead);
                    //state.sb.Append(Encoding.Default.GetString(state.buffer, 0, bytesRead));                
                    // Get the rest of the data.
                    //Console.Write(Encoding.Default.GetString(state.buffer, 0, bytesRead));              
                    client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
                }
                else
                {
                    // All the data has arrived; put it in response.
                    Console.WriteLine("begin write html");
                    if (state.sb.Length > 1)
                    {
                        Console.WriteLine("begin writing");
                        response = state.sb.ToString();
                        Console.WriteLine("begin write html");
                        // Console.WriteLine("state sb.length:{0}",state.sb.Length);
                        // state.sb.Remove(0, state.sb.Length);
                        // state.sb = null;
                    }

                    receiveDone.Set();
                }
                Console.WriteLine("then end for current Receive data size:{0}", bytesRead);
            }

            catch (Exception e)
            {
                isCompleted = false;
                receiveDone.Set();
                Log.WriteEventLog(url + "\n" + e.StackTrace + "\n" + e.Message);
                return;
            }
        }



        public void Close()
        {
            if (socket != null)
            {
                if (socket.Connected)
                {
                    socket.Shutdown(SocketShutdown.Both);
                    socket.Close();
                }
            }
        }

        #region IRequest 成员


        public WebHeaderCollection Header
        {
            get
            {
                throw new Exception("The method or operation is not implemented.");
            }
            set
            {
                throw new Exception("The method or operation is not implemented.");
            }
        }

        #endregion
    }
}
